#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QDebug>

TcpServer::TcpServer(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::TcpServer)
{
    // 初始化界面
    ui->setupUi(this);

    // 设置 TCP 端口
    tcpPort = 6666;

    // 创建 TCP 服务器对象
    tcpServer = new QTcpServer(this);

    // 连接 TCP 服务器的 newConnection 信号到 sendMessage 槽函数
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendMessage()));
}

TcpServer::~TcpServer()
{
    delete ui;
}

// 初始化服务器的函数
void TcpServer::initServer()
{
    // 设置每次发送的数据块大小
    payloadSize = 64*1024;
    // 初始化总字节数、已写字节数和待写字节数
    TotalBytes = 0;
    bytesWritten = 0;
    bytesToWrite = 0;

    // 设置服务器状态标签的文本 -- ui界面设置了可不用在代码中再次设值
    ui->serverStatusLabel->setText(tr("请选择需要发送的文件"));
    // 重置进度条-- ui界面设置了可不用在代码中再次设值
    ui->progressBar->reset();

    // 启用打开文件按钮，禁用发送文件按钮即未选择文件之前仅“打开按钮”有效，发送按钮无效，打开文件后仅发送按钮有效
    ui->serverOpenBtn->setEnabled(true);
    ui->serverSendBtn->setEnabled(false);

    // 关闭 TCP 服务器
    tcpServer->close();
}

// 处理文件被拒绝接收的情况
void TcpServer::refused()
{
    // 关闭 TCP 服务器
    tcpServer->close();
    // 更新服务器状态标签的文本
    ui->serverStatusLabel->setText("对方拒绝接收文件！！！");
}

// 处理窗口关闭事件
void TcpServer::closeEvent(QCloseEvent *)
{
    // 调用关闭按钮点击事件处理函数
    on_serverColseBtn_clicked();
}

// 发送消息（文件）的函数
void TcpServer::sendMessage()
{
    // （未选文件之前）刚开始禁用发送按钮
    ui->serverSendBtn->setEnabled(false);

    // 获取最新连接的客户端
    clientConnection = tcpServer->nextPendingConnection();

    // 连接客户端的 bytesWritten 信号到 updateClientProgress 槽函数
    connect(clientConnection, SIGNAL(bytesWritten(qint64)), this, SLOT(updateClientProgress(qint64)));

    // 更新服务器状态标签的文本
    ui->serverStatusLabel->setText(tr("开始发送文件%1！").arg(theFileName));

    // 打开要发送的文件
    localFile = new QFile(fileName);
    if (!localFile->open(QFile::ReadOnly)) {
        QMessageBox::warning(this, "应用程序", tr("无法读取文件%1：\n%2").arg(fileName).arg(localFile->errorString()));
        return;
    }

    // 获取文件大小
    TotalBytes = localFile->size();
    // 创建 QDataStream 用于数据输出
    QDataStream sendOut(&outBlock, QIODevice::WriteOnly);
    // 设置 QDataStream 的版本
    sendOut.setVersion(QDataStream::Qt_6_7);
    // 开始计时
    elapsedTime.start();
    // 获取文件名（去掉路径）
    QString currentFile = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);

    // 先预留空间用于存储文件总大小和文件名大小
    sendOut << qint64(0); // 数据总大小，占用 0-7 字节
    sendOut << qint64(0); // 文件名大小，占用 8-15 字节
    sendOut << currentFile;

    // 计算文件总字节数（包含头部信息）
    TotalBytes += outBlock.size();

    // 移动到数据块开头，写入正确的文件总大小和文件名大小
    sendOut.device()->seek(0);
    sendOut << TotalBytes; // 数据总大小
    sendOut << qint64((outBlock.size() - sizeof(qint64)*2)); // 文件名大小

    // 向客户端发送部分数据
    bytesToWrite = TotalBytes - clientConnection->write(outBlock);
    // 清空数据块
    outBlock.resize(0);
}

// 更新客户端进度的函数
void TcpServer::updateClientProgress(qint64 numBytes)
{
    // 处理事件，防止界面卡顿
    qApp->processEvents();
    // 累加已写字节数
    bytesWritten += (int)numBytes;
    if (bytesToWrite > 0) {
        // 从文件中读取一部分数据
        outBlock = localFile->read(qMin(bytesToWrite, payloadSize));
        // 向客户端发送读取的数据
        bytesToWrite -= (int)clientConnection->write(outBlock);
        // 清空数据块
        outBlock.resize(0);
    } else {
        // 关闭文件
        localFile->close();
    }

    // 设置进度条的最大值和当前值
    ui->progressBar->setMaximum(TotalBytes);
    ui->progressBar->setValue(bytesWritten);

    // 计算发送时间和速度等信息
    qint64 useTime = elapsedTime.elapsed();
    double speed = bytesWritten / useTime;
    ui->serverStatusLabel->setText(tr("已发送 %1MB (%2MB/s)\n共%3MB 已用时:%4秒\n估计剩余时间：%5秒")
                                       .arg(bytesWritten / (1024*1024))
                                       .arg(speed*1000 / (1024*1024), 0, 'f')
                                       .arg(TotalBytes / (1024 * 1024))
                                       .arg((useTime/1000, 0, 'f'))
                                       .arg(TotalBytes/speed/1000 - useTime/1000, 0, 'f'));

    if (bytesWritten == TotalBytes) {
        // 发送完成，关闭文件和服务器
        localFile->close();
        tcpServer->close();
        ui->serverStatusLabel->setText(tr("发送文件%1，成功").arg(theFileName));
    }
}

// 打开文件按钮点击处理函数
void TcpServer::on_serverOpenBtn_clicked()
{
    // 打开文件选择对话框，选择要发送的文件
    fileName = QFileDialog::getOpenFileName(this);
    if (!fileName.isEmpty()) {
        // 获取文件名（去掉路径）
        theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);
        // 更新服务器状态标签的文本
        ui->serverStatusLabel->setText(tr("要发送的文件为：").arg(theFileName));
        // 启用发送文件按钮，禁用打开文件按钮
        ui->serverSendBtn->setEnabled(true);
        ui->serverOpenBtn->setEnabled(false);
    }
}


// 发送文件按钮点击处理函数
void TcpServer::on_serverSendBtn_clicked()
{
    // 开始监听
    if (!tcpServer->listen(QHostAddress::Any, tcpPort)) {
        // 打印错误信息
        qDebug() << tcpServer->errorString();
        close();
        return;
    }
    // 更新服务器状态标签的文本
    ui->serverStatusLabel->setText("等待对方接收......");
    // 发送文件名信号
    emit sendFileName(theFileName);
}


// 关闭服务器按钮点击处理函数
void TcpServer::on_serverColseBtn_clicked()
{
    // 关闭 TCP 服务器
    if (tcpServer->isListening()) {
        tcpServer->close();
        if (localFile->isOpen())
            localFile->close();
        clientConnection->abort();
        /*  QTcpSocket 类中，abort() 方法的主要作用是中止当前的连接操作。
         *  它会立即关闭套接字，丢弃任何未发送或未接收的数据，并重置套接字的内部状态。 */
    }
    //关闭当前窗口，不用指定对象，默认为当前窗口
    close();
}
